package order

import (
	j "encoding/json"
	"fmt"
	"time"
	"trade/config"
	"trade/db"
	"trade/huobiapi"
	c "trade/huobiapi/client"
	"trade/logger"
	"trade/model"
)

type PreOrder struct {
	Price     float64 //建议下单价
	FlatPrice float64 //建议平仓价
	Direction string  //enum {buy,sell}
}

type HuobiOrder struct {
	conf       *config.Config
	OrderChan  chan PreOrder
	HasOrder   bool
	Market     *huobiapi.Market
	Client     *huobiapi.Client
	ReqKlineID int64
	OrderTimes int64
	markets    map[string]int
	db         *db.DB
}

func NewHuobiOrder(configPath string) (*HuobiOrder, error) {
	conf, err := config.NewConfig(configPath)
	if err != nil {
		return &HuobiOrder{}, err
	}
	market, err := huobiapi.NewMarket()
	if err != nil {
		return &HuobiOrder{}, err
	}
	client, err := huobiapi.NewClient(conf.TradeConf.AppKey, conf.TradeConf.AppSecret)
	if err != nil {
		return &HuobiOrder{}, err
	}
	return &HuobiOrder{conf, make(chan PreOrder, 1), false, market,
		client, 0, 0, map[string]int{"buy": 0, "sell": 0},
		db.NewDB("config")}, nil
}

func (this *HuobiOrder) Subscribe() {
	logger.Log.Debug("订阅深度信息")
	_ = this.Market.Subscribe(fmt.Sprintf("market.%s.depth.step0", this.conf.TradeConf.Contract), func(topic string, json *huobiapi.JSON) {
		// 收到数据更新时回调
		b, _ := j.Marshal(json)
		var dep model.DepthReturn
		_ = j.Unmarshal(b, &dep)
		// logger.Log.Debug("当前总的卖单总价值 $", dep.StatsAllAsk(), this.markets["sell"])
		// logger.Log.Debug("当前总的买单总价值 $", dep.StatsAllBids(), this.markets["buy"])
		if !this.HasOrder {
			if this.markets["sell"]+this.markets["buy"] < 3 {
				if dep.StatsAllAsk() < dep.StatsAllBids() {
					// logger.Log.Debug("卖单大于买单")
					this.markets["buy"]++
				} else {
					// logger.Log.Debug("卖单小于买单")
					this.markets["sell"]++
				}
			} else {
				index, err := this.Client.ContractIndexInfo(this.conf.TradeConf.Symbol)
				if err != nil {
					logger.Log.Error(err)
					return
				}
				go this.HandleOrder(dep, index)
				this.markets["buy"] = 0
				this.markets["sell"] = 0
			}
		}
	})
	// 进入阻塞等待，这样不会导致进程退出
	this.Market.Loop()
}

func (this *HuobiOrder) CreateOrder() {
	for {
		select {
		case o := <-this.OrderChan:
			logger.Log.Debug("系统推荐下单")
			//下单操作
			ores, err := this.Client.Order(c.Order{
				Symbol:         this.conf.TradeConf.Symbol,
				ContractType:   this.conf.TradeConf.ContractType,
				ContractCode:   this.conf.TradeConf.ContractCode,
				ClientOrderId:  time.Now().Unix(),
				Price:          o.Price,
				Volume:         this.conf.TradeConf.Volume,
				Direction:      o.Direction,
				Offset:         "open",
				LeverRate:      this.conf.TradeConf.LeverRate,
				OrderPriceType: "limit",
			})
			if err != nil {
				logger.Log.Error("下单失败:", err)
				this.HasOrder = false
				continue
			}
			if ores.Status == "ok" {
				this.OrderTimes++
				logger.Log.Infof("系统第 %d 次帮您自动下单", this.OrderTimes)
				//继续监听订单状态
				this.ListeningOrder(o, c.QueryOrder{OrderID: fmt.Sprintf("%d", ores.Data.OrderID), Symbol: this.conf.TradeConf.Symbol},
					false, 60)
			} else {
				logger.Log.Error("下单失败:", err)
			}
			this.HasOrder = false
			logger.Log.Debug("等待下一单")
		}
	}
}

func (this *HuobiOrder) ListeningOrder(preOrder PreOrder, o c.QueryOrder, isProfitOrder bool, timeout int64) (profitOrderResult bool) {
	profitOrderResult = true
	msg := "监听订单："
	if isProfitOrder {
		msg = "监听盈利订单："
	}
	logger.Log.Debug(msg, o.OrderID)
	start := time.Now().Unix()
	for {
		prifitDirection := ""
		if preOrder.Direction == "sell" {
			prifitDirection = "buy"
		} else {
			prifitDirection = "sell"
		}
		detail, err := this.Client.OrderDetail(o)
		if err != nil {
			logger.Log.Error("获取详情出错~1秒后重试", err)
			time.Sleep(1 * time.Second)
			continue
		}
		if detail.Status == c.STATUS_ALL_SUCCESS {
			if !isProfitOrder {
				//不是盈利单
				//全部成交 则挂盈利单
				profitOrder, err := this.Client.Order(c.Order{
					Symbol:         this.conf.TradeConf.Symbol,
					ContractType:   this.conf.TradeConf.ContractType,
					ContractCode:   this.conf.TradeConf.ContractCode,
					ClientOrderId:  time.Now().Unix(),
					Price:          preOrder.FlatPrice,
					Volume:         this.conf.TradeConf.Volume,
					Direction:      prifitDirection,
					Offset:         "close", //平仓单
					LeverRate:      this.conf.TradeConf.LeverRate,
					OrderPriceType: "limit", //只做maker单,post only下单只受用户持仓数量限制,
				})
				if err != nil {
					logger.Log.Error(err)
					break
				}
				if !this.ListeningOrder(preOrder, c.QueryOrder{OrderID: fmt.Sprintf("%d", profitOrder.Data.OrderID), Symbol: this.conf.TradeConf.Symbol},
					true, this.conf.TradeConf.CoverTime) {
					preOrder.FlatPrice = preOrder.Price
					continue
				}
				break
			} else {
				profitOrderResult = true
				logger.Log.Debug("盈利单已完成")
				break
			}
		}

		if isProfitOrder && detail.Status == c.STATUS_ALL_CANCEL {
			//如果 盈利单手动介入取消 监控等手动操作完成再进行自动化操作
			err = this.CheckPosition(time.Now().Unix() - this.conf.TradeConf.CoverTime)
			if err == nil {
				break
			}
			logger.Log.Debug(err)
		}

		//超时未成交则撤单 TODO 假设 都能成交 没有部分成交情况
		if time.Now().Unix()-start > timeout {
			if this.Client.CancelOrder(o) {
				if isProfitOrder {
					profitOrderResult = false
				} else {
					this.OrderTimes--
					break
				}
			}
		}
		time.Sleep(1 * time.Second)
	}
	return
}

func (this *HuobiOrder) CheckPosition(start int64) error {
	lastCover := &CoverPriceHistory{}
	firstStart := true
	for {
		data, err := this.Client.PositionInfo(this.conf.TradeConf.Symbol)
		if err != nil {
			time.Sleep(2 * time.Second)
			logger.Log.Debug("请求过于频繁~", err)
			continue
		}
		if len(data) < 1 {
			return nil
		}
		var holder model.Position
		for _, h := range data {
			if h.Direction == this.conf.TradeConf.Direction {
				holder = h
				break
			}
		}
		if holder.Volume == 0 {
			return nil
		}
		if firstStart {
			firstStart = false
			if holder.Volume <= float64(this.conf.TradeConf.Volume) {
				//第一次加载 db 没有补仓过 补仓过 就读取db历史数据
				lastCover.Reset(this.db)
			}
		}
		lastCover.Load(this.db)
		if lastCover.OrderStart <= 0 {
			lastCover.OrderStart = start
		}
		//根据指数信息 来判断当前 利润应该多少
		r, err := GetRatio(this.conf.TradeConf.Symbol, "1h")
		if err != nil {
			logger.Log.Error(err)
			time.Sleep(1 * time.Second)
			continue
		}
		danbian, direct := r.GetQuanWangSuggest()
		profit := 0.05
		if danbian {
			if direct == this.conf.TradeConf.Direction {
				//如果是对的方向 加大盈利设置
				profit = r.GetProfit()
			}
			logger.Log.Debug("单边行情 实时方向:", direct)
		} else {
			logger.Log.Debug("震荡行情 实时方向:", direct)
		}
		logger.Log.Debugf(this.conf.TradeConf.Direction+" 当前有持仓情况，持仓价:$%.3f 盈利率:%.3f%s 最新价:$%.3f", holder.CostHold, holder.ProfitRate*100, "%", holder.LastPrice)
		if holder.ProfitRate > profit {
			volume := int64(holder.Available)
			direction := "sell"
			if holder.Direction == "sell" {
				direction = "buy"
			}
			for {
				//进行强制平仓操作
				d, err := this.Client.ForceOrder(c.ForceOrder{
					Volume:       volume,
					Symbol:       this.conf.TradeConf.Symbol,
					ContractType: this.conf.TradeConf.ContractType,
					Direction:    direction,
				})
				if err != nil || d.Status != "ok" {
					logger.Log.Error(err)
					break
				}
				lastCover.Reset(this.db)
				return nil
			}
		}
		if this.CoverOrder(start, holder, lastCover) {
			start = time.Now().Unix()
		}
		time.Sleep(1 * time.Second)
	}
}

//请求K线5分钟柱状体数据
func (this *HuobiOrder) HandleOrder(dep model.DepthReturn, index model.Index) {
	err := this.CheckPosition(time.Now().Unix())
	if err != nil {
		logger.Log.Error(err)
		return
	}
	this.ReqKlineID++
	logger.Log.Debug("根据K线进行下单可行性分析")
	this.HasOrder = true
	minAsk := dep.GetOpenAsk()
	maxBids := dep.GetOpenBids()
	price := 0.00
	flatPrice := 0.00
	currentTime := time.Now().Unix()
	kline, err := this.Market.RequestKline(fmt.Sprintf("market.%s.kline.%s", this.conf.TradeConf.Contract, "15min"),
		this.ReqKlineID, currentTime-15*60, currentTime)
	if err != nil {
		logger.Log.Error(err)
		this.HasOrder = false
		return
	}
	b, _ := j.Marshal(kline)
	var kli model.Kline
	_ = j.Unmarshal(b, &kli)
	r, err := GetRatio(this.conf.TradeConf.Symbol, "1h")
	if err != nil {
		logger.Log.Error(err)
		return
	}
	danbian, direct := r.GetQuanWangSuggest()
	if danbian {
		logger.Log.Debug("单边行情 实时方向:", direct)
	} else {
		logger.Log.Debug("震荡行情 实时方向:", direct)
	}
	if this.markets["sell"] > this.markets["buy"] {
		if danbian && direct == "buy" {
			this.HasOrder = false
			return
		}
		flatPrice = minAsk * (1 - this.conf.TradeConf.Profit)
		price = minAsk

		if !kli.CanOrder(price, flatPrice, "sell", index) || this.conf.TradeConf.Direction != "sell" {
			logger.Log.Warning("空单方向判断不确定性 卖出价 ", price, " 平仓价", flatPrice, "指数价", index.IndexPrice)
			this.HasOrder = false
			return
		}
		logger.Log.Debugf("当前深度总卖单大于于总买单人 建议卖出 价格即将下跌 建议卖出价：$ %.3f 建议平仓价:$ %.3f", price, flatPrice)
		this.OrderChan <- PreOrder{Price: price, FlatPrice: flatPrice, Direction: "sell"}
	} else {
		if danbian && direct == "sell" {
			this.HasOrder = false
			return
		}
		flatPrice = maxBids * (1 + this.conf.TradeConf.Profit)
		price = maxBids
		if !kli.CanOrder(price, flatPrice, "buy", index) || this.conf.TradeConf.Direction != "buy" {
			logger.Log.Warning("买单方向判断不确定性 买入价", price, " 平仓价", flatPrice, "指数价", index.IndexPrice)
			this.HasOrder = false
			return
		}
		logger.Log.Infof("当前深度总卖单小于总买单人 建议买入 价格即将上涨 建议买入价：$%.3f 建议平仓价:$%.3f", price, flatPrice)
		this.OrderChan <- PreOrder{Price: price, FlatPrice: flatPrice, Direction: "buy"}
	}
}
